package com.net.xpay.common.service.impl;

import com.google.common.base.Preconditions;
import com.net.xpay.common.domain.DailyTaskRecord;
import com.net.xpay.common.domain.partner.Partner;
import com.net.xpay.common.domain.partner.PartnerSettleRecord;
import com.net.xpay.common.enums.partner.PartnerSettlePeriod;
import com.net.xpay.common.model.search.PartnerSearchModel;
import com.net.xpay.common.model.search.PartnerSettleRecordSearchModel;
import com.net.common.util.SerializationUtil;
import com.net.common.util.StreamUtil;
import com.net.xpay.common.manager.partner.PartnerManager;
import com.net.xpay.common.manager.partner.PartnerSettleRecordManager;
import com.net.xpay.common.service.PartnerSettleInnerService;
import com.net.xpay.common.service.PartnerSettleService;
import com.net.shard.ShardDataSource;
import com.net.shard.ShardDataSourceConfigurer;
import com.net.shard.ShardUtil;
import com.net.shard.domain.ShardConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;

/**
 * 2020/9/3 17:41

 */
@Slf4j
@Service
public class PartnerSettleServiceImpl implements PartnerSettleService {

    @Autowired
    private PartnerManager partnerManager;

    @Autowired
    private PartnerSettleRecordManager partnerSettleRecordManager;

    @Autowired
    private PartnerSettleInnerService partnerSettleInnerService;

    @Autowired
    @Qualifier("shardDataSource")
    private DataSource shardDataSource;


    @Override
    public void statPartnerSettleRecord(LocalDate day, DailyTaskRecord record) {
        PartnerSearchModel searchModel = new PartnerSearchModel();
        List<Partner> partners = partnerManager.list(searchModel);
        record.setTotalCount((long) partners.size());
        log.info("statPartnerSettleRecord partnerIds={}", StreamUtil.map(partners, Partner::getId));
        if (CollectionUtils.isEmpty(partners)) {
            return;
        }

        //遍历合伙人一个个的生成统计记录 注意保证幂等性统计过的就不要再统计了
        for (Partner partner : partners) {
            try {
                doStatPartnerSettleRecord(partner, day);
            } catch (Exception e) {
                log.error("statPartnerSettleRecord error|partnerId={}", partner.getId(), e);
                record.setFailedCount(record.getFailedCount() + 1L);
                continue;
            }

            record.setSuccessCount(record.getSuccessCount() + 1L);
        }
    }

    @Override
    public void settleMonthPartnerBenefit() {
        PartnerSearchModel searchModel = new PartnerSearchModel();
        searchModel.setSettlePeriod(PartnerSettlePeriod.MONTH);
        List<Partner> partners = partnerManager.list(searchModel);
        log.info("settleMonthPartnerBenefit start partnerIds={}", StreamUtil.map(partners, Partner::getId));

        if (CollectionUtils.isEmpty(partners)) {
            return;
        }

        for (Partner partner : partners) {
            partnerSettleInnerService.doSettlePartnerBenefit(partner);
        }
        log.info("settleMonthPartnerBenefit end partnerIds={}", StreamUtil.map(partners, Partner::getId));
    }

    @Override
    public void settleDayPartnerBenefit() {
        PartnerSearchModel searchModel = new PartnerSearchModel();
        searchModel.setSettlePeriod(PartnerSettlePeriod.DAY);
        List<Partner> partners = partnerManager.list(searchModel);
        log.info("settleDayPartnerBenefit start partnerIds={}", StreamUtil.map(partners, Partner::getId));

        if (CollectionUtils.isEmpty(partners)) {
            return;
        }

        for (Partner partner : partners) {
            partnerSettleInnerService.doSettlePartnerBenefit(partner);
        }
        log.info("settleDayPartnerBenefit end partnerIds={}", StreamUtil.map(partners, Partner::getId));
    }


    private void doStatPartnerSettleRecord(Partner partner, LocalDate day) {
        //判断下是否已经统计过了
        if (hasStated(partner.getId(), day)) {
            log.warn("doSettlePartnerBenefit has stated|partnerId={}|day={}", partner.getId(), day);
            return;
        }

        ShardDataSourceConfigurer shardDataSourceConfigurer = ((ShardDataSource) shardDataSource).getShardDataSourceConfigurer();
        Map<Long, ShardConfig> shardConfigMap = shardDataSourceConfigurer.getShardConfigMap();
        Preconditions.checkArgument(CollectionUtils.isNotEmpty(shardConfigMap.keySet()));

        PartnerSettleRecord record = PartnerSettleRecord.getInstance(day, partner.getId());
        for (Long shardId : shardConfigMap.keySet()) {
            try {
                ShardUtil.setShardContext(shardId);
                log.info("doStatPartnerSettleRecordInShard start in shard={}|partnerId={}", shardId, partner.getId());
                doStatPartnerSettleRecordInShard(partner, day, record);
                log.info("doStatPartnerSettleRecordInShard end in shard={}|partnerId={}", shardId, partner.getId());
            } finally {
                ShardUtil.cleanShardContext();
            }
        }

        partnerSettleRecordManager.save(record);
        log.info("doStatPartnerSettleRecord save record|record={}", SerializationUtil.obj2String(record));
    }

    private boolean hasStated(Long partnerId, LocalDate day) {
        PartnerSettleRecordSearchModel searchModel = new PartnerSettleRecordSearchModel();
        searchModel.setPartnerId(partnerId);
        searchModel.setDay(day);
        long count = partnerSettleRecordManager.searchCount(searchModel);
        return count > 0L;
    }

    private void doStatPartnerSettleRecordInShard(Partner partner, LocalDate day, PartnerSettleRecord record) {
        partnerSettleInnerService.doStatPartnerSettleRecord(partner, day, record);
    }
}
